﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TestcraftLibrary.CVar;
using Lidgren.Network;
using System.IO;
using System.Threading;
using System.Numerics;
using TestcraftLibrary.Network;
using TestcraftLibrary.Common;

namespace TestcraftServer
{
    class Server
    {
        ulong[][] primes =  {
                              new ulong[] {32416187567, 5},
                              new ulong[] {32416188223, 5},
                              new ulong[] {32416188809, 3},
                              new ulong[] {32416189391, 11},
                              new ulong[] {32416187627, 2},
                              new ulong[] {32416188227, 2},
                              new ulong[] {32416188839, 11},
                              new ulong[] {32416189459, 2},
                              new ulong[] {32416187651, 6},
                              new ulong[] {32416188241, 11},
                              new ulong[] {32416188859, 3},
                              new ulong[] {32416189469, 2},
                              new ulong[] {32416187659, 3},
                              new ulong[] {32416188257, 0},
                              new ulong[] {32416188877, 0},
                              new ulong[] {32416189493, 0},
                              new ulong[] {32416187701, 0},
                              new ulong[] {32416188269, 0},
                              new ulong[] {32416188887, 0},
                              new ulong[] {32416189499, 0},
                              new ulong[] {32416187719, 0},
                              new ulong[] {32416188271, 0},
                              new ulong[] {32416188899, 0},
                              new ulong[] {32416189511, 0},
                              new ulong[] {32416187737, 0},
                              new ulong[] {32416188331, 0},
                              new ulong[] {32416188949, 0},
                              new ulong[] {32416189573, 0},
                              new ulong[] {32416187747, 0},
                              new ulong[] {32416188349, 0},
                              new ulong[] {32416189019, 0},
                              new ulong[] {32416189633, 0},
                              new ulong[] {32416187761, 0},
                              new ulong[] {32416188367, 0},
                              new ulong[] {32416189031, 0},
                              new ulong[] {32416189657, 0},
                              new ulong[] {32416187773, 0},
                              new ulong[] {32416188397, 0},
                              new ulong[] {32416189049, 0},
                              new ulong[] {32416189669, 0},
                              new ulong[] {32416187827, 0},
                              new ulong[] {32416188449, 0},
                              new ulong[] {32416189061, 0},
                              new ulong[] {32416189681, 0},
                              new ulong[] {32416187863, 0},
                              new ulong[] {32416188451, 0},
                              new ulong[] {32416189063, 0},
                              new ulong[] {32416189717, 0},
                              new ulong[] {32416187893, 0},
                              new ulong[] {32416188491, 0},
                              new ulong[] {32416189079, 0},
                              new ulong[] {32416189721, 0},
                              new ulong[] {32416187899, 0},
                              new ulong[] {32416188499, 0},
                              new ulong[] {32416189081, 0},
                              new ulong[] {32416189733, 0},
                              new ulong[] {32416187927, 0},
                              new ulong[] {32416188517, 0},
                              new ulong[] {32416189163, 0},
                              new ulong[] {32416189753, 0},
                              new ulong[] {32416187929, 0},
                              new ulong[] {32416188527, 0},
                              new ulong[] {32416189181, 0},
                              new ulong[] {32416189777, 0},
                              new ulong[] {32416187933, 0},
                              new ulong[] {32416188583, 0},
                              new ulong[] {32416189193, 0},
                              new ulong[] {32416189853, 0},
                              new ulong[] {32416187953, 0},
                              new ulong[] {32416188589, 0},
                              new ulong[] {32416189231, 0},
                              new ulong[] {32416189859, 0},
                              new ulong[] {32416188601, 0},
                              new ulong[] {32416189261, 0},
                              new ulong[] {32416189867, 0},
                              new ulong[] {32416187987, 0},
                              new ulong[] {32416188647, 0},
                              new ulong[] {32416189277, 0},
                              new ulong[] {32416189877, 0},
                              new ulong[] {32416188011, 0},
                              new ulong[] {32416188689, 0},
                              new ulong[] {32416189291, 0},
                              new ulong[] {32416189909, 0},
                              new ulong[] {32416188037, 0},
                              new ulong[] {32416188691, 0},
                              new ulong[] {32416189321, 0},
                              new ulong[] {32416189919, 0},
                              new ulong[] {32416188113, 0},
                              new ulong[] {32416188697, 0},
                              new ulong[] {32416189349, 0},
                              new ulong[] {32416189987, 0},
                              new ulong[] {32416188127, 0},
                              new ulong[] {32416188767, 0},
                              new ulong[] {32416189361, 0},
                              new ulong[] {32416190039, 0},
                              new ulong[] {32416188191, 0},
                              new ulong[] {32416188793, 0},
                              new ulong[] {32416189381, 0},
                              new ulong[] {32416190071, 0},
                              new ulong[] {22801763489, 0},
                              new ulong[] {10000000000000000051, 0},
                              new ulong[] {10000000000000000087, 0},
                              new ulong[] {10000000000000000091, 0},
                              new ulong[] {10000000000000000097, 0},
                              new ulong[] {10000000000000000099, 0},
                              new ulong[] {10000000000000000147, 0},
                              new ulong[] {10000000000000000169, 0},
                              new ulong[] {10000000000000000273, 5},
                              new ulong[] {10000000000000000297, 5},
                              new ulong[] {10000000000000000307, 2},
                              new ulong[] {14300012000066000189, 2},
                              new ulong[] {14300012000066000287, 6},
                              new ulong[] {14300012000066000303, 5},
                              new ulong[] {14300012000066000413, 6},
                              new ulong[] {14300012000066000441, 3},
                              new ulong[] {14300012000066000479, 3},
                              new ulong[] {14300012000066000491, 3},
                              new ulong[] {14300012000066000717, 2},
                              new ulong[] {14300012000066000731, 3},
                              new ulong[] {14300012000066000809, 7},
                              new ulong[] {11451882000066780383, 5},
                              new ulong[] {11451882000066780529, 7},
                              new ulong[] {11451882000066780541, 6},
                              new ulong[] {11451882000066780593, 3},
                              new ulong[] {11451882000066780617, 3},
                              new ulong[] {11451882000066780631, 3},
                              new ulong[] {11451882000066780707, 2},
                              new ulong[] {11451882000066780761, 7},
                              new ulong[] {11451882000066780821, 2},
                              new ulong[] {11451882000066780829,  2}
                          };
        private Thread messageThread;
        public CVarSystem CVarSystem { get; set; }
        private NetServer netServer;

        private Dictionary<long, PlayerConnection> players;


        public Server()
        {
            this.CVarSystem = new CVarSystem();
            this.players = new Dictionary<long, PlayerConnection>();
        }



        private void initCvar()
        {
            this.CVarSystem.Startup();

            this.CVarSystem.Set(new CVar("Port", "The local port that the server is bound to", 34545, false));
            this.CVarSystem.Set(new CVar("Name", "The name of the world", "World", false));
            this.CVarSystem.Set(new CVar("Seed", "The randomization seed used to generate terrain", (int)DateTime.Now.Ticks, false));

            string configPath = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "server.properties");

            if (!System.IO.File.Exists(configPath))
            {
                StreamWriter writer = null;
                try
                {
                    writer = new StreamWriter(configPath);
                    foreach (CVar cvar in this.CVarSystem)
                    {
                        writer.WriteLine("# " + cvar.Description);
                        writer.WriteLine(string.Format("{0}={1}", cvar.Name, cvar.GetValueAsString()));
                    }
                }
                finally
                {
                    if (writer != null)
                        writer.Close();
                }
            }
            else
            {
                System.IO.StreamReader reader = null;
                try
                {
                    reader = new StreamReader(configPath);
                    string line;
                    int lineCount = 0;
                    while (!reader.EndOfStream)
                    {
                        line = reader.ReadLine();
                        if (line[0] != '#')
                        {
                            string[] keyValuePair = line.Split('=');
                            if (keyValuePair.Length != 2)
                            {
                                throw new InvalidDataException(string.Format("Configuration is invalid at line {0}", lineCount));
                            }

                            CVar cvar = this.CVarSystem.Get(keyValuePair[0]);
                            if (cvar == null)
                            {
                                throw new InvalidDataException(string.Format("Unknown configuration at line {0}: {1}", lineCount, keyValuePair[0]));
                            }
                            else
                            {
                                cvar.SetValueAsString(keyValuePair[1]);
                            }
                        }
                        lineCount++;
                    }
                }
                finally
                {
                    if (reader != null)
                        reader.Close();
                }
            }
        }

        public void Initialize()
        {
            this.initCvar();

            NetPeerConfiguration config = new NetPeerConfiguration("testcraft");
            config.Port = this.CVarSystem.Get("Port").GetInt();
            this.netServer = new NetServer(config);

        }

        public void Start()
        {
            this.netServer.Start();
            this.messageThread = new Thread(this.MessageLoop);
            this.messageThread.IsBackground = true;
            this.messageThread.Start();
        }

        private void MessageLoop()
        {
            while (true)
            {
                this.netServer.MessageReceivedEvent.WaitOne();
                NetIncomingMessage message = netServer.ReadMessage();

                switch (message.MessageType)
                {
                    case NetIncomingMessageType.VerboseDebugMessage:
                    case NetIncomingMessageType.DebugMessage:
                    case NetIncomingMessageType.WarningMessage:
                    case NetIncomingMessageType.ErrorMessage:
                        Console.WriteLine(message.ReadString());
                        break;
                    case NetIncomingMessageType.StatusChanged:
                        NetConnectionStatus status = (NetConnectionStatus)message.ReadByte();
                        if (status == NetConnectionStatus.Connected)
                        {
                            Console.WriteLine(NetUtility.ToHexString(message.SenderConnection.RemoteUniqueIdentifier) + " connected!");
                            message.SenderConnection.Tag = new PlayerConnection();
                        }

                        break;
                    case NetIncomingMessageType.Data:
                        PlayerConnection connection = (PlayerConnection)message.SenderConnection.Tag;
                        if(connection.state == PlayerConnection.ConnectionState.Authentication)
                        {
                            connection.PlayerName = message.ReadString();
                            connection.Token = message.ReadBytes(16);
                            // Validera användarnamn och token.
                            connection.state = PlayerConnection.ConnectionState.Play;
                        }
                        else if (connection.state == PlayerConnection.ConnectionState.Play)
                        {
                            MessageType type = (MessageType)message.ReadByte();
                            if (type == MessageType.ChunkData)
                            {
                                NetOutgoingMessage response = netServer.CreateMessage();

                                byte chunkCount = message.ReadByte();
                                for (int i = 0; i < chunkCount; i++)
                                {
                                    int x = message.ReadInt32();
                                    int z = message.ReadInt32();
                                    List<BlockDescriptorAdd> blocks = GiveMeSomeBlocks();
                                }
                            }
                        }
                        break;
                }

            }
        }

        private List<BlockDescriptorAdd> GiveMeSomeBlocks()
        {
            List<BlockDescriptorAdd> sureThing = new List<BlockDescriptorAdd>(16 * 16 * 250);
            Random randy = new Random((int)DateTime.Now.Ticks);
            for (short blockx = 0; blockx < 16; blockx++)
            {
                for (short blockz = 0; blockz < 16; blockz++)
                {
                    int height = 250; //randy.Next(240, 255);

                    for (short blocky = 0; blocky < height; blocky++)
                    {
                        {
                            //sureThing.Add(new Block((ushort)randy.Next(0, 2))
                            //{
                            //    X = blockx,
                            //    Y = blocky,
                            //    Z = blockz
                            //});
                            BlockDescriptorAdd descriptor;
                            descriptor.Type = (ushort)0;
                            descriptor.Location.X = blockx;
                            descriptor.Location.Y = blocky;
                            descriptor.Location.Z = blockz;
                            sureThing.Add(descriptor);
                        }
                    }
                }
            }

            return sureThing;
        }

    }
}
